﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.IO;

namespace Vapeur
{
    public partial class Form1 : Form
    {
        Engine currentEngine;
        int currentIndex = 0;
        bool autoPlayFlag;
        WebClient client;
        LibVlc vlc;
        List<string> searchList;
        SearchType currentSearchType = SearchType.Artist;
        Video currentVideo;

        public Form1()
        {
            InitializeComponent();
            vlc = new LibVlc();
            vlc.Initialize();
            vlc.VideoOutput = this.panelVideo;
            searchList = new List<string>();


        }

        #region button clicks

        private void buttonArtist_Click(object sender, EventArgs e)
        {
            GetArtistInfo(((ToolStripButton)sender).Text, true, SearchType.Artist);
        }

        private void toolStripButtonClear_Click(object sender, EventArgs e)
        {
            currentIndex = 0;
            this.listBoxPlaylist.Items.Clear();
            this.Stop();
        }

        private void listBoxPlaylist_DoubleClick(object sender, EventArgs e)
        {
            this.Stop();
            this.currentIndex = ((ListBox)sender).SelectedIndex;
            this.Play();
        }

        private void toolStripDropDownButtonStop_Click(object sender, EventArgs e)
        {
            this.Stop();
        }

        private void listBoxTracks_DoubleClick(object sender, EventArgs e)
        {
            AddVideo((Track)listBoxTracks.SelectedItem);
        }

        private void buttonBack_Click(object sender, EventArgs e)
        {
            if (vlc.IsPlaying)
            {
                vlc.Stop();
                vlc.Play();
            }
        }

        private void buttonForward_Click(object sender, EventArgs e)
        {
            this.Next();
        }

        /// <summary>
        /// Delete track from playlist
        /// </summary>
        private void listBoxPlaylist_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Delete)
            {
                this.listBoxPlaylist.Items.Remove(this.listBoxPlaylist.SelectedItem);
                this.SavePlaylist("tempPlaylist.xml"); //Autosave
            }
        }

        private void buttonFullScreen_Click(object sender, EventArgs e)
        {
            this.vlc.Fullscreen = true;
        }


        private void buttonArtistBack_Click(object sender, EventArgs e)
        {

        }


        private void buttonPlay_Click(object sender, EventArgs e)
        {
            if (timer1.Enabled == true)
            {
                this.togglePlayPause();
                return;
            }

            this.Stop();
            if (this.listBoxPlaylist.SelectedIndex < 0)
            {
                this.currentIndex = 0;
            }
            else
            {
                this.currentIndex = listBoxPlaylist.SelectedIndex;
            }
            this.Play();
        }

        private void togglePlayPause()
        {
            if (vlc.IsPlaying)
            {
                vlc.Pause();
            }
            else
            {
                vlc.Play();
            }
        }

        #endregion

        private void GetArtistInfo(string artist, bool isNewSearch, SearchType type)
        {
            currentEngine = new Engine(artist, type);

            if (currentEngine.Artist == null)
            {
                if (type == SearchType.Artist)
                {
                    MessageBox.Show("Artist not found");
                }
                else if (type == SearchType.Tag)
                {
                    MessageBox.Show("No tracks tagged with " + artist);
                }
                return;
            }

            this.toolStrip1.Items.Clear();
            this.listBoxTracks.Items.Clear();

            if (isNewSearch && type == SearchType.Artist)
            {
                searchList.Add(currentEngine.Artist);
            }


            this.toolStripButtonBack.Enabled = (this.searchList.Count > 1);


            ToolStripLabel label = new ToolStripLabel(currentEngine.Artist);
            label.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            label.ForeColor = System.Drawing.SystemColors.HighlightText;
            this.toolStrip1.Items.Add(label);

            foreach (Track track in currentEngine.Tracks)
            {
                this.listBoxTracks.Items.Add(track);
            }

            for (int i = 0; i < 5; i++)
            {
                if (currentEngine.Similar.Count > i)
                {
                    ToolStripButton button = new ToolStripButton(currentEngine.Similar[i]);

                    button.Click += new EventHandler(this.buttonArtist_Click);

                    this.toolStrip1.Items.Add(button);
                }
            }

        }




        private int WeightedRandom(int max, int add)
        {
            int maximum = ((max * (max + 1)) / 2) + add * max;
            Random rng = new Random();
            int ret = rng.Next(maximum);
            for (int i = 1; i <= max; i++)
            {
                if (ret < ((i * (i + 1)) / 2))
                    return (max - i);
            }
            return max;
        }

        private void AddVideo()
        {
            Engine localEngine;
            if (currentEngine != null)
            {
                int artistRand = this.WeightedRandom(currentEngine.Similar.Count, 0);

                if (artistRand == 0)
                {
                    localEngine = currentEngine;
                }
                else
                {
                    artistRand--;
                    localEngine = new Engine(currentEngine.Similar[artistRand], SearchType.Artist);
                }

                int trackRand = this.WeightedRandom(localEngine.Tracks.Count, 0);
                System.Console.WriteLine(artistRand + ":" + trackRand);



                Video video = new Video();
                video.Track = localEngine.Tracks[trackRand];
                if (video.VideoURL == null)
                    this.AddVideo();
                else
                    this.listBoxPlaylist.Items.Add(video);
            }
        }

        private void AddVideo(Track track)
        {
            Video video = new Video();
            video.Track = track;
            if (video.VideoURL == null)
                MessageBox.Show("No video found for " + video.Track.Artist + " - " + video.Track.Name);
            else
            {
                this.listBoxPlaylist.Items.Add(video);
                //this.SavePlaylist("tempPlaylist.xml"); //Autosave
            }

        }

        private void AddVideo(string url)
        {
            Video video = new Video();
            video.YouTubeURL = url;
                this.listBoxPlaylist.Items.Add(video);
                //this.SavePlaylist("tempPlaylist.xml"); //Autosave
        }

        private void Play()
        {
            if (currentIndex < 0)
                currentIndex = 0;

            if (currentIndex < this.listBoxPlaylist.Items.Count)
            {
                Video video = (Video)this.listBoxPlaylist.Items[currentIndex];
                this.currentVideo = video;
                this.listBoxPlaylist.SelectedIndex = currentIndex;

                if (client != null)
                {
                    client.CancelAsync();
                    client.Dispose();
                }

                client = new WebClient();
                client.DownloadFileAsync(new Uri(video.VideoURL), video.Filename);


                client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);


                vlc.PlaylistClear();

                vlc.AddTarget(video.Filename);
                //vlc.Initialize();

                this.linkLabel1.Text = "\n" + video.Title;
                this.linkLabel1.Links.Clear();
                this.linkLabel1.Links.Add(0, 0, "http://www.youtube.com/watch?v=" + video.YouTubeID);
                this.linkLabel1.Visible = true;

                this.toolStrip2.Items.Clear();
                if (video.Track != null)
                {
                    if (video.Track.Artist != null)
                    {
                        ToolStripButton button = new ToolStripButton(video.Track.Artist);
                        button.Font = new Font(button.Font, FontStyle.Bold);
                        button.Click += new EventHandler(this.buttonArtist_Click);
                        this.toolStrip2.Items.Add(button);
                    }


                    ToolStripLabel label = new ToolStripLabel(video.Track.Name);
                    this.toolStrip2.Items.Add(label);
                }

                this.autoPlayFlag = true;
                this.timer1.Enabled = true;
            }
            else
                MessageBox.Show("End of playlist");
        }

        private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            int value;
            if (e.ProgressPercentage < 0)
                value = 0;
            else if (e.ProgressPercentage > 100)
                value = 100;
            else
                value = e.ProgressPercentage;

            this.progressBarDownload.Value = value;

            //if percentage dlded is more than 5, start playing
            if (value > 5)
            {
                if (autoPlayFlag)
                {
                    vlc.Play();
                    autoPlayFlag = false;
                }
            }
        }



        private void Stop()
        {
            this.timer1.Enabled = false;

            if (this.vlc != null)
            {
                this.vlc.Stop();
                this.vlc.PlaylistClear();
            }
            if (this.client != null)
            {
                this.client.CancelAsync();
                this.client.Dispose();
            }

            this.progressBarDownload.Value = 0;
            this.progressBarVideo.Value = 0;

            //Video video = (Video)this.listBoxPlaylist.Items[currentIndex];
            ///TODO: this is a hack because i couldnt delete a file straight after it has stopped playing.
            ///this hack still leaves most recent flv in directory. But will be deleted on next cycle.
            foreach (string filename in Directory.GetFiles(Directory.GetCurrentDirectory()))
            {
                FileInfo info = new FileInfo(filename);
                ///IMPORTANT!!!  this could be dangerous if not done properly.
                ///maybe perform another check to see if the file is in the playlist?? would slow program.
                if (info.Extension == ".flv")
                {


                    if (File.Exists(filename))
                    {
                        try
                        {
                            File.Delete(filename);
                        }
                        catch (IOException e)
                        {
                            Console.WriteLine(
                                "{0}: The deletion of {1} could not " +
                                "be performed because the specified " +
                                "file is locked.",
                                e.GetType().Name, filename);
                        }
                    }

                }
            }
        }



        private void timer1_Tick(object sender, EventArgs e)
        {

            int value = (int)(vlc.PositionGet * 100);
            if (value < 0)
                value = 0;
            else if (value > 100)
                value = 100;
            else
                progressBarVideo.Value = value;

            if (!vlc.IsPlaying && !autoPlayFlag && value > 80)
            {
                this.Next();
            }
        }

        private void Next()
        {
            this.Stop();

            //Remapp index
            if (listBoxPlaylist.Items.Contains(currentVideo))
            {
                currentIndex = listBoxPlaylist.Items.IndexOf(currentVideo);
            }

            currentIndex++;
            this.Play();

            ///This adds a random track if the playlist is coming to an end. Disabling it cos its buggy.
            //if (listBoxPlaylist.Items.Count < (this.currentIndex + 2))
            //{
            //    this.AddVideo();
            //}
        }

        #region drag drop stuff


        private void listBoxTracks_MouseDown(object sender, MouseEventArgs e)
        {
            //if (listBoxTracks.Items.Count == 0)
            //    return;

            //int index = listBoxTracks.IndexFromPoint(e.X, e.Y);
            //string s = listBoxTracks.Items[index].ToString();
            //DragDropEffects dde1 = DoDragDrop(s,
            //    DragDropEffects.All);

            //if (dde1 == DragDropEffects.All)
            //{
            //    listBoxTracks.Items.RemoveAt(listBoxTracks.IndexFromPoint(e.X, e.Y));
            //}
        }

        //private void listBoxPlaylist_DragOver(object sender, DragEventArgs e)
        //{
        //    e.Effect = DragDropEffects.All;
        //}

        private void listBoxPlaylist_DragDrop(object sender, DragEventArgs e)
        {
            Video vid = (Video)e.Data.GetData(typeof(Video));
            int indexPos = ((ListBox)sender).IndexFromPoint(((ListBox)sender).PointToClient(new Point(e.X, e.Y)));
            if (indexPos > -1)
            {
                ((ListBox)sender).Items.Insert(indexPos, vid);

            }
            else
            {
                ((ListBox)sender).Items.Add(vid);

                //currentIndex = listBoxPlaylist.Items.Count - 1;
            }

            this.SavePlaylist("tempPlaylist.xml"); //autosave
        }

        private void listBoxPlaylist_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && e.Clicks == 1)
            {
                if (((ListBox)sender).Items.Count == 0)
                    return;

                int index = ((ListBox)sender).IndexFromPoint(e.X, e.Y);
                if (index < 0)
                    return;

                object s = ((ListBox)sender).Items[index];
                DragDropEffects dde1 = DoDragDrop(s,
                    DragDropEffects.Move);

                if (s == ((ListBox)sender).Items[index])
                    ((ListBox)sender).Items.RemoveAt(index);
                else
                    ((ListBox)sender).Items.RemoveAt(index + 1);
            }
        }

        private void listBoxPlaylist_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(Video)))
                e.Effect = DragDropEffects.Move;
            else
                e.Effect = DragDropEffects.None;
        }

        #endregion

    

        private void toolStripDropDownButtonAuto_Click(object sender, EventArgs e)
        {
            this.GeneratePlaylist(false);

            this.Shuffle(this.listBoxPlaylist.Items);

        }

        private void GeneratePlaylist(bool randomTracks)
        {
            int artistLimit = 3;
            int trackLimit = 10;
            List<Engine> engineList = new List<Engine>();
            engineList.Add(currentEngine);

            currentIndex = 0;
            this.listBoxPlaylist.Items.Clear();
            this.Stop();

            for (int i = 0; i < artistLimit; i++)
            {
                if (currentEngine.Similar.Count > i)
                {
                    engineList.Add(new Engine(currentEngine.Similar[i], SearchType.Artist));
                }
            }


            for (int i = 0; i < trackLimit / artistLimit; i++)
            {
                for (int j = 0; j < artistLimit; j++)
                {
                    this.AddVideo(engineList[j].Tracks[i]);
                }
            }

            for (int i = 0; i < trackLimit % artistLimit; i++)
            {
                this.AddVideo(engineList[i].Tracks[trackLimit / artistLimit]);
            }
        }

        private void Shuffle(ListBox.ObjectCollection elements)
        {
            Random rng = new Random();
            int n = elements.Count;
            while (n > 1)
            {
                int k = rng.Next(n);
                n--;
                object temp = elements[n];
                elements[n] = elements[k];
                elements[k] = temp;
            }
        }

        private void toolStripButtonShuffle_Click(object sender, EventArgs e)
        {
            this.Shuffle(this.listBoxPlaylist.Items);
        }

        private void toolStripButtonSave_Click(object sender, EventArgs e)
        {
            if (this.listBoxPlaylist.Items.Count > 0)
            {
                //TODO: Make filename relative
                SaveFileDialog sfd = new SaveFileDialog();

                sfd.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
                sfd.FilterIndex = 1;
                sfd.RestoreDirectory = true;


                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    this.SavePlaylist(sfd.FileName);
                }
            }
        }

        private void toolStripButtonLoad_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
            ofd.FilterIndex = 1;

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                listBoxPlaylist.Items.Clear();
                this.LoadPlaylist(ofd.FileName);
            }
        }

        private void LoadPlaylist(string filename)
        {

            System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(Video[]), new Type[] { typeof(Video), typeof(Track) });

            // A FileStream is needed to read the XML document.
            FileStream fs = new FileStream(filename, FileMode.Open);
            System.Xml.XmlReader reader = new System.Xml.XmlTextReader(fs);

            object o = null;

            try
            {

                o = ser.Deserialize(reader);

            }

            catch (Exception ex)
            {

                MessageBox.Show("Error loading playlist: " + ex.Message);

            }

            reader.Close();

            listBoxPlaylist.Items.Clear();

            listBoxPlaylist.Items.AddRange((object[])o);

            listBoxPlaylist.Refresh();

        }

        private void SavePlaylist(string filename)
        {

            Video[] list = new Video[this.listBoxPlaylist.Items.Count];
            this.listBoxPlaylist.Items.CopyTo(list, 0);


            using (TextWriter writer = File.CreateText(filename))
            {

                System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(Video[]), new Type[] { typeof(Video), typeof(Track) });

                x.Serialize(writer, list);


                writer.Close();
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.SavePlaylist("tempPlaylist.xml");  //autosave
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //Load last playlist
            //if (File.Exists("tempPlaylist.xml"))
            //{
            //    this.LoadPlaylist("tempPlaylist.xml");
            //}
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            string target = e.Link.LinkData as string;
            System.Diagnostics.Process.Start(target);
            e.Link.Visited = true;
        }

        private void search()
        {
            if (this.toolStripTextBox1.Text != "")
            {
                this.GetArtistInfo(this.toolStripTextBox1.Text, true, currentSearchType);
                this.listBoxTracks.Enabled = true;
            }
        }

        private void toolStripSplitButton1_ButtonClick(object sender, EventArgs e)
        {
            this.search();
        }

        private void artistToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.toolStripSplitButton1.Text = "Artist";
            this.currentSearchType = SearchType.Artist;
        }

        private void tagToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.toolStripSplitButton1.Text = "Tag";
            this.currentSearchType = SearchType.Tag;
        }

        private void toolStripButtonBack_Click(object sender, EventArgs e)
        {
            if (this.searchList.Count > 1)
            {
                this.searchList.RemoveAt(this.searchList.Count - 1);
                this.GetArtistInfo(this.searchList[this.searchList.Count - 1], false, SearchType.Artist);
            }
        }

        private void toolStripTextBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Return)
            {
                this.search();
            }
        }

        private void buttonManualSearch_Click(object sender, EventArgs e)
        {
            Track track = new Track();
            track.SearchType = SearchType.Manual;
            track.Name = this.textBoxManualSearch.Text;
            AddVideo(track);
        }

        private void buttonYouTubeAdd_Click(object sender, EventArgs e)
        {
            this.AddVideo(this.textBoxYouTubeAdd.Text);
        }
    }
}